﻿//========================================================= 
/**@file ComICCardCmdDriver.h
 * @brief 三合一读卡器驱动逻辑
 * 
 * @date 2016-05-04   21:09:37
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_BP8903CMDDRIVER_H_
#define _LIBZHOUYB_BP8903CMDDRIVER_H_
//--------------------------------------------------------- 
#include "../CommonCmdDriver.h"
#include "ICCardCmdDriver.h"

#include "../../device/ComIC_ReaderDevAdapter.h"
using zhou_yb::application::device::ComIC_ReaderDevAdapter;
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace driver {
//--------------------------------------------------------- 
/// 三合一命令驱动
class ComICCardCmdDriver : 
    public CommonCmdDriver, 
    public BaseDevAdapterBehavior<IInteractiveTrans>
{
protected:
    ComICCardCmdAdapter _cmdAdapter;
    ComContactICCardDevAdapter _contactIC;
    ComContactlessICCardDevAdapter _contactlessIC;
    ComPsamICCardDevAdapter _psam1;
    ComPsamICCardDevAdapter _psam2;
    ComIC_ReaderDevAdapter _reader;
    ICCardCmdDriver _icDriver;

    Ref<IICCardDevice> _slotContactIC;
    Ref<IICCardDevice> _slotContactlessIC;
    Ref<IICCardDevice> _slotPsam1IC;
    Ref<IICCardDevice> _slotPsam2IC;

    LC_CMD_METHOD(RemovePSAM)
    {
        _icDriver.ReleaseDevice();
        _icDriver.SelectDevice(_contactlessIC);
        _icDriver.SelectDevice(_contactIC);
        return true;
    }
    LC_CMD_METHOD(InsertPSAM)
    {
        _icDriver.SelectDevice(_psam1, "0");
        _icDriver.SelectDevice(_psam2, "1");
        return true;
    }
public:
    ComICCardCmdDriver() : CommonCmdDriver()
    {
        _errGetter.Add(_icDriver)
            .New()
            .Add("CONTACT", _contactIC)
            .Add("CONTACTLESS", _contactlessIC)
            .Add("PSAM1", _psam1)
            .Add("PSAM2", _psam2)
            .Add("Reader", _reader)
            .End()
            .New().Add("IC_CMD", _cmdAdapter);

        select_helper<LoggerSetter::SelecterType>::select(_logSetter),
            _cmdAdapter, _contactIC, _contactlessIC, _psam1, _psam2, _reader;

        _contactIC.SelectDevice(_cmdAdapter);
        _contactlessIC.SelectDevice(_cmdAdapter);
        _psam1.SelectDevice(_cmdAdapter);
        _psam2.SelectDevice(_cmdAdapter);
        _reader.SelectDevice(_cmdAdapter);

        _icDriver.SelectDevice(_contactlessIC);
        _icDriver.SelectDevice(_contactIC);
        _icDriver.SelectDevice(_psam1, "0");
        _icDriver.SelectDevice(_psam2, "1");

        _slotContactIC = _contactIC;
        _slotContactlessIC = _contactlessIC;
        _slotPsam1IC = _psam1;
        _slotPsam2IC = _psam2;

        _RegisteCMD(ComICCardCmdDriver, SlotToName);
        _RegisteCMD(ComICCardCmdDriver, NameToSlot);
        _RegisteCMD(ComICCardCmdDriver, ScanForCardUID);
        _RegisteExceptCMD(ComICCardCmdDriver);
        _RegisteAdapterCMD(ComICCardCmdDriver);

        list_t<Ref<ComplexCommand> > cmds;
        _icDriver.GetCommand("", cmds);
        // Adapter接口会冲突,故用Unique方式注册子模块
        CommandCollection::UniqueRegisteCommand(*this, cmds);

        /* 选择卡槽时支持根据名称进行选择 */
        Ref<ComplexCommand> transformSlotCmd = _RegisteCMD(ComICCardCmdDriver, TransformSLOT);
        transformSlotCmd->Bind(LookUp("ResetError"), ComplexCommand::RunOnFailed);
        _RegisteCMD(ComICCardCmdDriver, GetSLOT)->PrefixBind(transformSlotCmd, ComplexCommand::RunOnAlways);
        _RegisteCMD(ComICCardCmdDriver, ActiveSLOT)->PrefixBind(transformSlotCmd, ComplexCommand::RunOnAlways);

        LookUp("SelectSLOT")->PrefixBind(transformSlotCmd);

        /* 自动寻卡时不需要检测PSAM卡 */
        Ref<ComplexCommand> waitForCardCmd = LookUp("WaitForCard");
        Ref<Command> insertPsamCmd = _MakeCMD(ComICCardCmdDriver, InsertPSAM);
        Ref<Command> removePsamCmd = _MakeCMD(ComICCardCmdDriver, RemovePSAM);
        waitForCardCmd->PrefixBind(removePsamCmd);
        waitForCardCmd->PrefixBind(transformSlotCmd, ComplexCommand::RunOnAlways);
        waitForCardCmd->Bind(insertPsamCmd, ComplexCommand::RunOnAlways);
    }
    LC_CMD_EXCEPT()
    {
        EXCEPT_CMD(NameToSlot);
        EXCEPT_CMD(SlotToName);
        EXCEPT_CMD(ActiveSLOT);
        EXCEPT_CMD(GetSLOT);
        EXCEPT_CMD(TransformSLOT);
        return true;
    }
    LC_CMD_ADAPTER_INVOKE(IInteractiveTrans, _cmdAdapter);
    /**
     * @brief 将IC卡SLOT从名称转为对应的索引
     * @date 2017-03-25 11:22
     * 
     * @param [in] SLOT : string 直接使用名称,或者索引
     * - 名称:
     *  - CONTACTLESS 非接
     *  - CONTACT 接触
     *  - PSAM1
     *  - PSAM2
     * .
     */
    LC_CMD_METHOD(TransformSLOT)
    {
        string slot;
        if(!arg.Get("SLOT", slot))
        {
            _logErr(DeviceError::ArgErr, "缺少参数:[SLOT]");
            return false;
        }
        ByteArray slotArray(slot.c_str());
        IStringArguments::ArgType& argVal = arg["SLOT"];
        if(StringConvert::Compare("CONTACTLESS", slotArray, true))
        {
            argVal.Value = "1";
        }
        else if(StringConvert::Compare("CONTACT", slotArray, true))
        {
            argVal.Value = "2";
        }
        else if(StringConvert::Compare("PSAM1", slotArray, true))
        {
            argVal.Value = "3";
        }
        else if(StringConvert::Compare("PSAM2", slotArray, true))
        {
            argVal.Value = "4";
        }
        else
        {
            return false;
        }
        return true;
    }
    /**
     * @brief 将IC卡SLOT从索引转为对应的名称
     * @date 2017-03-25 11:22
     * 
     * @param [in] SLOT : uint 索引
     * - 索引和名称对应关系:
     *  - 1:CONTACTLESS 非接
     *  - 2:CONTACT 接触
     *  - 3:PSAM1
     *  - 4:PSAM2
     * .
     *
     * @retval SLOT_NAME : string 索引对应的名称
     */
    LC_CMD_METHOD(SlotToName)
    {
        uint slot = arg["SLOT"].To<uint>(0);
        string slotName = "";
        switch(slot)
        {
        case 1:
            slotName = "CONTACTLESS";
            break;
        case 2:
            slotName = "CONTACT";
            break;
        case 3:
            slotName = "PSAM1";
            break;
        case 4:
            slotName = "PSAM2";
            break;
        default:
            _logErr(DeviceError::ArgRangeErr, "卡槽号错误");
            return false;
        }
        rlt.Put("SLOT_NAME", slotName);
        return true;
    }
    /**
     * @brief 将对应的名称转为索引
     * @date 2017-07-30 12:40
     * 
     * @param [in] SLOT_NAME : string 名称
     * 
     * @retval SLOT : uint 转换后的索引
     */
    LC_CMD_METHOD(NameToSlot)
    {
        string slotName;
        if(!arg.Get("SLOT_NAME", slotName))
        {
            _logErr(DeviceError::ArgErr, "缺少参数:[SLOT]");
            return false;
        }
        ByteArray slotArray(slotName.c_str(), slotName.length());
        string slot = "";
        if(StringConvert::Compare("CONTACTLESS", slotArray, true))
        {
            slot = "1";
        }
        else if(StringConvert::Compare("CONTACT", slotArray, true))
        {
            slot = "2";
        }
        else if(StringConvert::Compare("PSAM1", slotArray, true))
        {
            slot = "3";
        }
        else if(StringConvert::Compare("PSAM2", slotArray, true))
        {
            slot = "4";
        }
        else
        {
            _logErr(DeviceError::ArgFormatErr, "卡槽号错误");
            return false;
        }
        rlt.Put("SLOT", slot);
        return true;
    }
    /**
     * @brief 激活卡槽为上电状态
     * @date 2017-07-30 11:12
     * 
     * @param [in] SLOT : uint 需要设置激活的卡槽号
     */
    LC_CMD_METHOD(ActiveSLOT)
    {
        uint slot = arg["SLOT"].To<uint>(0);
        switch(slot)
        {
        case 1:
            _contactlessIC.ActiveSLOT();
            break;
        case 2:
            _contactIC.ActiveSLOT(0);
            break;
        case 3:
            _psam1.ActiveSLOT(0x01);
            break;
        case 4:
            _psam2.ActiveSLOT(0x02);
            break;
        default:
            return false;
        }
        return _icDriver.SelectSLOT(slot);
    }
    /**
     * @brief 获取指定卡槽的IICCardDevice接口
     * @date 2017-03-25 11:18
     * 
     * @param [in] SLOT : string 直接使用名称,或者索引(参见TransformSLOT),如果值错误,则返回所有的SLOT
     *
     * @retval CONTACT : Ref<T> 接触
     * @retval CONTACTLESS : Ref<T> 非接
     * @retval PSAM1 : Ref<T> PSAM卡1
     * @retval PSAM2 : Ref<T> PSAM卡2
     */
    LC_CMD_METHOD(GetSLOT)
    {
        uint slot = arg["SLOT"].To<uint>(0);
        pointer p = NULL;
        switch(slot)
        {
        case 1:
            p = reinterpret_cast<pointer>(&(_slotContactlessIC));
            break;
        case 2:
            p = reinterpret_cast<pointer>(&(_slotContactIC));
            break;
        case 3:
            p = reinterpret_cast<pointer>(&(_slotPsam1IC));
            break;
        case 4:
            p = reinterpret_cast<pointer>(&(_slotPsam2IC));
            break;
        default:
            p = reinterpret_cast<pointer>(&(_slotContactlessIC));
            rlt.Put("CONTACTLESS", ArgConvert::ToString(p));

            p = reinterpret_cast<pointer>(&(_slotContactIC));
            rlt.Put("CONTACT", ArgConvert::ToString(p));

            p = reinterpret_cast<pointer>(&(_slotPsam1IC));
            rlt.Put("PSAM1", ArgConvert::ToString(p));

            p = reinterpret_cast<pointer>(&(_slotPsam2IC));
            rlt.Put("PSAM2", ArgConvert::ToString(p));

            p = NULL;
            break;
        }
        if(p != NULL)
        {
            rlt.Put("IICCardDevice", ArgConvert::ToString(p));
        }
        return true;
    }
    /**
     * @brief 扫描当前所有的非接卡片
     * @date 2017-04-24 22:32
     * 
     * @param [in] UID : hex 需要连接的卡片UID
     * @warning 如果没有该参数,则枚举所有的UID并返回
     *
     * @param [in] MaxCardCount : size_t [default:5] 需要扫描的最大卡片数
     * @param [in] MaxRetryCount : size_t [default:3] 扫描失败时的最大错误尝试次数
     * @param [in] Interval : uint [default:50] 连续扫描卡片时的轮询间隔
     * @param [in] RetryInterval : uint [default:200] 扫描失败时的轮询间隔
     * 
     * @retval UID : hex 枚举到的所有卡片UID
     * @retval ATR : hex 枚举到的所有卡片ATR
     * @retval Count : size_t 枚举到的所有卡片数目
     */
    LC_CMD_METHOD(ScanForCardUID)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();

        string sUid;
        ByteBuilder cardUid(8);
        bool isUid = arg.Get("UID", sUid);
        if(isUid)
        {
            DevCommand::FromAscii(sUid.c_str(), cardUid);
            LOGGER(_log << "UID:<" << sUid << ">\n");
        }

        uint intervalMs = arg["Interval"].To<uint>(50);
        uint retryIntervalMs = arg["RetryInterval"].To<uint>(200);
        size_t maxCardCount = arg["MaxCardCount"].To<size_t>(5);
        size_t maxRetryCount = arg["MaxRetryCount"].To<size_t>(3);

        LOGGER(_log << "IntervalMs:<" << intervalMs << ">\n";
        _log << "RetryIntervalMs:<" << retryIntervalMs << ">\n";
        _log << "MaxCard:<" << maxCardCount << ">\n";
        _log << "RetryCount:<" << maxRetryCount << ">\n");

        // 如果设备不支持多张卡,关闭天线时会报错
        if(!_reader.HaltAerial())
        {
            _logErr(DeviceError::OperatorErr, "关闭天线失败");
            maxCardCount = 1;
        }
        else
        {
            Timer::Wait(intervalMs);
        }

        ByteBuilder atr(8);
        ByteBuilder uid(8);
        ByteBuilder lastUid(8);
        size_t retryCount = 0;

        list_t<ByteBuilder> uidlist;
        list_t<ByteBuilder>::iterator itr;

        string currentUid;
        string currentAtr;
        size_t count = 0;

        for(size_t i = 0;i < maxCardCount; ++i)
        {
            if(InterruptBehavior::Implement(_icDriver.Interrupter))
            {
                ResetErr();
                _logErr(DeviceError::OperatorInterruptErr);
                return false;
            }

            uid.Clear();
            atr.Clear();

            if(!_contactlessIC.ActiveMultiICCard(0, NULL, &uid, &atr))
            {
                ++retryCount;
                LOGGER(_log << "第<" << retryCount << ">次重试...\n");
                if(retryCount >= maxRetryCount)
                {
                    bool isOK = ((!isUid) && (count > 0));
                    if(isOK)
                    {
                        rlt.Put("Count", ArgConvert::ToString(count));
                    }
                    return isOK;
                }

                Timer::Wait(retryIntervalMs);
                continue;
            }

            currentUid = ArgConvert::ToString(uid);
            currentAtr = ArgConvert::ToString(atr);

            LOGGER(_log << "UID:<" << currentUid << ">\n");
            LOGGER(_log << "ATR:<" << currentAtr << ">\n");

            if(isUid)
            {
                if(StringConvert::Contains(uid, cardUid))
                {
                    LOGGER(_log.WriteLine("找到卡片"));

                    rlt.Put("UID", currentUid);
                    rlt.Put("ATR", currentAtr);
                    rlt.Put("Count", "1");

                    // 激活非接
                    _icDriver.SelectSLOT(1);
                    return true;
                }
            }
            else
            {
                for(itr = uidlist.begin();itr != uidlist.end(); ++itr)
                {
                    // 已经枚举过
                    if(StringConvert::Compare(*itr, uid))
                    {
                        break;
                    }
                }

                // 新的卡片
                if(itr == uidlist.end())
                {
                    rlt.Put("UID", currentUid);
                    rlt.Put("ATR", currentAtr);
                    ++count;
                }
            }

            retryCount = 0;

            _contactlessIC.SetHaltStatus();

            Timer::Wait(intervalMs);

            lastUid = uid;
            uidlist.push_back(uid);
        }
        bool isOK = (!isUid) && (count > 0);
        if(isOK)
        {
            rlt.Put("Count", ArgConvert::ToString(count));
        }
        return isOK;
    }
};
//--------------------------------------------------------- 
} // nemespace driver
} // namespace application
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_BP8903CMDDRIVER_H_
//========================================================= 